﻿using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using FStore.Utils;

namespace FStore.Formats
{
    /// <summary>
    /// Allows an object to be stored using xml-serialisation
    /// </summary>
    public sealed class XmlSerFormat : IFlatFormat
    {
        #region IFlatFormat Members

        public string Decompose<T>(T obj)
        {
            return Decompose(typeof(T), obj);
        }

        public string Decompose(Type type, object obj)
        {
            // Param checking
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            if (obj == null)
            {
                throw new ArgumentNullException("obj");
            }

            if (obj.GetType() != type)
            {
                throw new ArgumentException("obj is not of correct type", "obj");
            }

            try
            {
                // Create the serialiser
                var ns = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
                var ser = new XmlSerializer(type);
                var settings = new XmlWriterSettings();
                // Don't include unnecessary xml declaration
                settings.OmitXmlDeclaration = true;
                using (var stream = new StringWriter())
                {
                    using (var writer = XmlWriter.Create(stream, settings))
                    {
                        // Serialise and return the Unicode string
                        ser.Serialize(writer, obj);
                        return Encoding.Unicode.GetString(stream.Encoding.GetBytes(stream.ToString()));
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(
                    "Failed to Decompose object of type '{0}' - {1}".UFormat(
                        obj.GetType().FullName,
                        ex.Message));
            }
        }

        public T Compose<T>(string str)
        {
            return (T)Compose(typeof(T), str);
        }

        #endregion

        public object Compose(Type type, string str)
        {
            // Param checking
            if (string.IsNullOrEmpty(str))
            {
                throw new ArgumentException("Cannot compose string as it was null or empty");
            }

            if (type == null)
            {
                throw new ArgumentException("Cannot compose string as provided type was null");
            }

            try
            {
                // Create the serialiser
                var ns = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
                var ser = new XmlSerializer(type);
                var settings = new XmlReaderSettings();

                using (var stream = new StringReader(str))
                {
                    // Return the deserialised object
                    return ser.Deserialize(stream);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(
                    "Failed to compose object of type '{0}' from string '{1}' - {2}".UFormat(
                        type.FullName,
                        str,
                        ex.Message));
            }
        }
    }
}